
#include <glmm/glmm.h>
#include <stdio.h>
#include <SDL.h>
#include <GL/glu.h>


glmmDevice* device = 0;
unsigned program;
int position0;
int position1;
int color0;
int color1;
int vl;

glmmIVertexBuffer* vb;
glmmIIndexBuffer* ib;
glmmIVertexDefinition *vd;

struct Vertex
{
    float x, y, z;
    float r, g, b, a;
};

GLMMVERTEXDESCRIPTION desc[] = {
    { 0, 0,  sizeof (Vertex), 3, GLMMDATATYPE_FLOAT, GLMMVERTEXTYPE_XYZ, 0, },
    //{ 0, 12, sizeof (Vertex), 4, GLMMDATATYPE_FLOAT, GLMMVERTEXTYPE_COLOR, 0, },
    NULL
};

bool InitDevice()
{
    device = new glmmDevice();
    glmmDeviceCapabilities caps;
    device->GetDeviceCapabilities(&caps);

    printf("DeviceCaps:\n");
    printf("GLVersion                   : %d.%d\n", GLMM_VERSION_MAJOR(caps.OpenGLVersion), GLMM_VERSION_MINOR(caps.OpenGLVersion));
    printf("Shading Language            : %d.%d\n", GLMM_VERSION_MAJOR(caps.ShadingLanguageVersion), GLMM_VERSION_MINOR(caps.ShadingLanguageVersion));
    printf("Non Power of to support     : %s\n", GLMM_CAPSMODESTRING(caps.NonPowerOfTwoTextures));
    printf("FramebufferObjects support  : %s\n", GLMM_CAPSMODESTRING(caps.FrameBufferObjects));
    printf("VertexbufferObjects support : %s\n", GLMM_CAPSMODESTRING(caps.VertexBufferObjects));
    printf("VertexShader                : %s\n", GLMM_CAPSMODESTRING(caps.VertexShader));
    printf("FragmentShader              : %s\n", GLMM_CAPSMODESTRING(caps.FragmentShader));
    return true;
}


bool CompileShader(unsigned shader, const char* filename)
{
    FILE* file = fopen(filename, "rb");
    if (!file)
      {
        printf("Unable to open file: %s\n", filename);
        return false;
      }
    fseek(file, 0, SEEK_END);
    long size = ftell(file);
    char* buffer = new char [size + 1];
    fseek(file, 0, SEEK_SET);
    fread(buffer, sizeof (char), size, file);
    fclose(file);
    buffer[size] = '\0';
    int length = size;

    glShaderSource(shader, 1, (const GLchar**) &buffer, &length);
    glCompileShader(shader);

    char compileLog[2048];
    glGetShaderInfoLog(shader, 2048, &length, compileLog);
    compileLog[length] = '\0';

    if (length != 0)
      {
        printf("CompileLog: %s\n%s\n", filename, compileLog);
      }

    return true;
}

void LinkProgram(unsigned program)
{
    int length;
    glLinkProgram(program);
    char linkLog[2048];
    glGetProgramInfoLog(program, 2048, &length, linkLog);
    linkLog[length] = '\0';

    if (length != 0)
      {
        printf("LinkLog: %s\n", linkLog);
      }

}

bool CreateShader()
{
    unsigned vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    unsigned fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    program = glCreateProgram();
    if (vertex_shader == 0 || fragment_shader == 0 || program == 0)
      {
        return false;
      }


    CompileShader(vertex_shader, "bin/test.vs");
    CompileShader(fragment_shader, "bin/test.fs");


    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);

    LinkProgram(program);


    position0 = glGetAttribLocation(program, "position0");
    position1 = glGetAttribLocation(program, "position1");
    color0 = glGetAttribLocation(program, "color0");
    color1 = glGetAttribLocation(program, "color1");
    vl = glGetUniformLocation(program, "vl");

    return true;
}

bool CreateData ()
{
    if (device->CreateVertexBuffer (sizeof(Vertex) * 3, GLMMUSAGE_STATIC, &vb) != GLMM_OK)
      {
        printf ("Unable to create VertexBuffer\n");
        return false;
      }
    
    if (device->CreateIndexBuffer (sizeof (unsigned short) * 3, GLMMDATATYPE_UNSIGNED_SHORT, GLMMUSAGE_STATIC, &ib) != GLMM_OK)
      {
        printf ("Unable to create IndexBufer\n");
        return false;
      }
    
    if (device->CreateVertexDefinition (desc, &vd))
      {
        printf ("Unable to create VertexDefinition\n");
        return false;
      }
    
    Vertex vertices [] = {
      { -50, -50, 0,    1, 0, 0, 1,},
      {   0,  50, 0,    1, 1, 0, 1,},
      {  50, -50, 0,    0, 1, 1, 1,},
    };
    
    unsigned short indices [] = {
                                 0, 1, 2,
    };
    
    
    void* data;
    if (vb->Lock (GLMMLOCK_WRITE, &data) != GLMM_OK)
      {
        printf ("Unable to lock vertex buffer\n");
        return false;
      }
    memcpy (data, vertices, sizeof (vertices));
    vb->Unlock ();
    
    if (ib->Lock (GLMMLOCK_WRITE, &data) != GLMM_OK)
      {
        printf ("Unable to lock index buffer\n");
        return false;
      }
    memcpy (data, indices, sizeof (indices));
    ib->Unlock ();
    
    return true;
}

void Render()
{
    glClearColor(0, 0, 0.5, 0);
    glClear(GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-1, 1, -0.75, 0.75, 1, 1024);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0, 0, 100, 0, 0, 0, 0, 1, 0);

    device->SetVertexDefinition (vd);
    device->SetStreamSource (0, vb);
    device->SetIndices (ib);
    
    
    if (device->DrawIndexedPrimitives (GLMMPRIMTYPE_TRIANGLES, 0, 3) != GLMM_OK)
      {
        printf ("Unable to render\n");
      }

}

int main(int argc, char** argv)
{
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);

    SDL_Surface* surface = SDL_SetVideoMode(1024, 768, 32, SDL_OPENGL | SDL_HWSURFACE);

    glewInit();

    if (!InitDevice() || !CreateData ())
      {
        SDL_Quit();
        return 0;
      }

    glViewport(0, 0, 1024, 768);

    bool running = true;
    SDL_Event e;
    while (running)
      {
        while (SDL_PollEvent(&e))
          {
            switch (e.type)
            {
                case SDL_KEYDOWN:
                    if (e.key.keysym.sym == SDLK_ESCAPE)
                      {
                        running = false;
                      }
                default:
                    break;
            }
          }

        Render();
        SDL_GL_SwapBuffers();
      }

    SDL_Quit();
    return 0;
}

